20. Асинхронные методы в JavaScript и их применение в Electron
Введение в асинхронность в JavaScript
Асинхронность — это одна из ключевых концепций JavaScript, которая позволяет выполнять задачи без блокировки основного потока выполнения. Это особенно полезно для операций, которые занимают время, таких как запросы к серверу, чтение файлов или выполнение сложных вычислений.
Основные инструменты для работы с асинхронностью:
- Callback-функции — функции, которые передаются в качестве аргументов и вызываются после завершения асинхронной операции.
- Promises (Обещания) — объекты, которые представляют результат асинхронной операции, которая может завершиться успешно или с ошибкой.
- Async/Await — синтаксический сахар для работы с Promises, который делает асинхронный код более читаемым.
Пример асинхронного кода в JavaScript
1. Использование Callback-функций
function fetchData(callback) {
setTimeout(() => {
callback("Данные получены!");
}, 1000);
}
fetchData((data) => {
console.log(data); // "Данные получены!" через 1 секунду
});
2. Использование Promises
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Данные получены!");
}, 1000);
});
}
fetchData()
.then((data) => {
console.log(data); // "Данные получены!" через 1 секунду
})
.catch((error) => {
console.error(error);
});
3. Использование Async/Await
async function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Данные получены!");
}, 1000);
});
}
async function main() {
try {
const data = await fetchData();
console.log(data); // "Данные получены!" через 1 секунду
} catch (error) {
console.error(error);
}
}
main();
Как работает async
?
Функция, объявленная с async
, всегда возвращает Promise.
Пример 1: async
всегда возвращает Promise
async function getNumber() {
return 42;
}
getNumber().then(console.log); // Выведет 42
Здесь getNumber()
вернет Promise, который имеет значение 42
.
Как работает await
?
Ключевое слово await
заставляет JavaScript ждать, пока Promise выполнится, и только после этого продолжает выполнение кода.
⚠️ Важно: await
можно использовать только внутри async
-функции.
Пример 2: Использование await
async function fetchData() {
console.log("Запрос данных...");
let result = await new Promise(resolve => setTimeout(() => resolve("Данные получены!"), 2000));
console.log(result);
}
fetchData();
console.log("Этот код выполнится сразу, не дожидаясь fetchData()");
Что произойдет?
fetchData()
запустится и выведет"Запрос данных..."
.- JavaScript не будет ждать завершения
fetchData()
и сразу выполнитconsole.log("Этот код выполнится сразу...")
. - Через 2 секунды в консоли появится
"Данные получены!"
.
Как async/await
заменяет then/catch
?
Рассмотрим пример с fetch
, который делает HTTP-запрос.
Современный способ: async/await
async function getTodo() {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
let data = await response.json();
console.log(data);
} catch (error) {
console.error("Ошибка:", error);
}
}
getTodo();
Обработка нескольких await
подряд
Если нужно выполнить несколько await
, они выполняются по очереди, что может замедлять код.
Плохой пример (медленный код)
async function getData() {
let user = await fetch("https://jsonplaceholder.typicode.com/users/1").then(res => res.json());
let posts = await fetch("https://jsonplaceholder.typicode.com/posts?userId=1").then(res => res.json());
console.log(user, posts);
}
getData();
Здесь второй fetch
начнется только после завершения первого, что неэффективно.
Оптимизированный вариант (параллельные запросы)
async function getData() {
let [user, posts] = await Promise.all([
fetch("https://jsonplaceholder.typicode.com/users/1").then(res => res.json()),
fetch("https://jsonplaceholder.typicode.com/posts?userId=1").then(res => res.json())
]);
console.log(user, posts);
}
getData();
Теперь оба запроса выполняются параллельно, экономя время.
Вывод
async
делает функцию асинхронной и всегда возвращает Promise.await
заставляет код ждать выполнения Promise перед продолжением.try/catch
удобнее для обработки ошибок, чем.catch()
.Promise.all()
помогает ускорить выполнение несколькихawait
.
Применение асинхронности в Electron с использованием Context Isolation
Теперь, когда мы разобрались с асинхронностью в JavaScript, давайте рассмотрим, как её можно использовать в Electron-приложении с включённой Context Isolation.
Шаг 1: Настройка проекта Electron
- Создайте базовое Electron-приложение с включённой
contextIsolation
и отключённойnodeIntegration
. - Добавьте файл предзагрузки (
preload.js
), который будет предоставлять рендереру доступ к асинхронным методам.
Пример index.js
:
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
mainWindow.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
Пример preload.js
:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('YourApp', {
performAsyncTask: () => ipcRenderer.invoke('perform-async-task'),
});
contextBridge.exposeInMainWorld('YourApp', {
performAsyncTask: async () => await ipcRenderer.invoke('perform-async-task'),
});
Шаг 2: Реализация асинхронной задачи в основном процессе
В основном процессе создадим асинхронную задачу, которая будет выполняться с задержкой и возвращать результат в рендерер.
Пример асинхронной задачи в index.js
:
ipcMain.handle('perform-async-task', async (e, data) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Асинхронная задача завершена!");
}, 2000); // 2000 мс == 2 с
});
});
Шаг 3: Использование асинхронной задачи в рендерере
Теперь, когда асинхронная задача настроена, мы можем вызвать её из рендерера и отобразить результат.
Пример renderer.js
:
async function runTask() {
try {
const result = await window.YourApp.performAsyncTask();
console.log(result); // "Асинхронная задача завершена!" через 2 секунды
document.getElementById('result').innerText = result;
} catch (error) {
console.error('Ошибка:', error);
}
}
document.getElementById('startTask').addEventListener('click', runTask);
Пример index.html
:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Асинхронные задачи в Electron</title>
</head>
<body>
<h1>Асинхронные задачи в Electron</h1>
<button id="startTask">Запустить задачу</button>
<p id="result"></p>
<script src="renderer.js"></script>
</body>
</html>
Практическое задание
- Создайте Electron-приложение с включённой
contextIsolation
и отключённойnodeIntegration
. - Реализуйте асинхронную задачу в основном процессе, которая имитирует загрузку данных с сервера (например, с задержкой в 2 секунды).
- Отобразите результат выполнения задачи в интерфейсе приложения.
- Добавьте обработку ошибок на случай, если задача завершится с ошибкой.
Заключение
Асинхронные методы в JavaScript — это мощный инструмент для выполнения задач без блокировки основного потока. В Electron с использованием Context Isolation можно безопасно выполнять асинхронные задачи, передавая данные между основным процессом и рендерером. В этом занятии мы рассмотрели, как реализовать асинхронные задачи в Electron и отобразить их результаты в интерфейсе приложения.